package cn.csc.module.pay.controller.app.order;

import cn.csc.framework.common.exception.util.ServiceExceptionUtil;
import cn.csc.framework.common.pojo.CommonResult;
import cn.csc.framework.common.pojo.PageResult;
import cn.csc.framework.common.util.collection.CollectionUtils;
import cn.csc.framework.pay.core.client.PayClient;
import cn.csc.framework.pay.core.client.PayCommonResult;
import cn.csc.framework.pay.core.client.dto.PayOrderUnifiedRespDTO;
import cn.csc.framework.pay.core.enums.channel.PayChannelEnum;
import cn.csc.framework.tenant.core.aop.TenantIgnore;
import cn.csc.module.pay.api.order.dto.PayOrderStatusReqDTO;
import cn.csc.module.pay.api.order.dto.PayOrderStatusRespDTO;
import cn.csc.module.pay.controller.admin.order.vo.PayOrderDetailsRespVO;
import cn.csc.module.pay.controller.admin.order.vo.PayOrderPageItemRespVO;
import cn.csc.module.pay.controller.admin.order.vo.PayOrderPageReqVO;
import cn.csc.module.pay.controller.admin.order.vo.PayOrderSubmitRespVO;
import cn.csc.module.pay.controller.app.order.vo.AppPayOrderCommSubmitReqVO;
import cn.csc.module.pay.controller.app.order.vo.AppPayOrderCreateReqVO;
import cn.csc.module.pay.controller.app.order.vo.AppPayOrderOuterSubmitReqVO;
import cn.csc.module.pay.convert.order.PayOrderConvert;
import cn.csc.module.pay.dal.dataobject.merchant.PayAppDO;
import cn.csc.module.pay.dal.dataobject.merchant.PayMerchantDO;
import cn.csc.module.pay.dal.dataobject.order.PayOrderDO;
import cn.csc.module.pay.dal.dataobject.order.PayOrderExtensionDO;
import cn.csc.module.pay.dal.mysql.order.PayOrderMapper;
import cn.csc.module.pay.enums.ErrorCodeConstants;
import cn.csc.module.pay.enums.order.PayOrderStatusEnum;
import cn.csc.module.pay.service.merchant.PayAppService;
import cn.csc.module.pay.service.merchant.PayChannelService;
import cn.csc.module.pay.service.merchant.PayMerchantService;
import cn.csc.module.pay.service.order.PayOrderExtensionService;
import cn.csc.module.pay.service.order.PayOrderService;
import cn.csc.module.pay.util.SignUtils;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.ObjectUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.annotation.security.PermitAll;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import static cn.csc.framework.common.pojo.CommonResult.error;
import static cn.csc.framework.common.pojo.CommonResult.success;
import static cn.csc.framework.common.util.servlet.ServletUtils.getClientIP;
import static cn.csc.module.pay.enums.DictTypeConstants.PAY_APP_SECRET;
import static cn.csc.module.pay.enums.ErrorCodeConstants.PAY_ORDER_WRONG_ARGS;
import static cn.hutool.core.date.DatePattern.NORM_DATETIME_PATTERN;

@Api(tags = "用户 APP - 支付订单")
@RestController
@RequestMapping("/pay/order")
@Validated
@Slf4j
@Setter
public class AppPayOrderController {


    @Resource
    private PayOrderService orderService;
    @Resource
    private PayOrderExtensionService orderExtensionService;
    @Resource
    private PayMerchantService merchantService;
    @Resource
    private PayAppService appService;

    @Resource
    private PayOrderMapper payOrderMapper;

    @Resource
    private PayChannelService channelService;

    @Resource
    private StringRedisTemplate stringRedisTemplate;


    @PostMapping("createOuter")
    @ApiOperation(value = "创建支付订单(外部)")
    public CommonResult<String> createOuter(@RequestBody AppPayOrderCreateReqVO reqVO){
        orderService.createPayOrder(PayOrderConvert.INSTANCE.convert(reqVO));
        return CommonResult.success();
    }

    @PostMapping("submitOuter")
    @ApiOperation(value = "提交支付订单(外部)")
    public CommonResult<PayOrderSubmitRespVO> submitOuter(@Validated @RequestBody AppPayOrderOuterSubmitReqVO reqVO){
        PayOrderSubmitRespVO respVO = orderService.submitPayOrderOuter(reqVO);
        return CommonResult.success(respVO);
    }

    @PostMapping("/submit")
    @ApiOperation(value = "提交支付订单")
    public PayCommonResult<PayOrderUnifiedRespDTO> submitPayOrder(HttpServletRequest request, @Validated @RequestBody AppPayOrderCommSubmitReqVO reqVO) {
        String sign = request.getParameter("sign");

        String secret = stringRedisTemplate.opsForValue().get(PAY_APP_SECRET + reqVO.getAppId());
        if (!StringUtils.hasLength(secret)){
            PayAppDO app = appService.getApp(reqVO.getAppId());
            secret = app.getSecret();
            stringRedisTemplate.opsForValue().set(PAY_APP_SECRET + app.getId(), secret);
        }
        // 校验参数签名
        if (!SignUtils.signDebug(reqVO, secret).equals(sign)){
            PayCommonResult.error(ErrorCodeConstants.PAY_ORDER_ERROR_SIGN);
        }
        return orderService.submitCommonPayOrder(reqVO, getClientIP());
    }


    @TenantIgnore
    @PermitAll
    @PostMapping("/submitOrder")
    @ApiOperation(value = "提交支付订单")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "appId", required = true, value = "应用编号", example = "1", dataTypeClass = String.class),
            @ApiImplicitParam(name = "userIp", value = "用户ip", required = true, example = "127.0.0.1", dataTypeClass = String.class),
            @ApiImplicitParam(name = "channelId", value = "渠道编号", required = true, example = "4", dataTypeClass = Integer.class),
            @ApiImplicitParam(name = "channelCode", value = "渠道编码", required = true, dataTypeClass = String.class),
            @ApiImplicitParam(name = "merchantOrderId", value = "商户订单号", required = true, example = "2023125648975", dataTypeClass = String.class),
            @ApiImplicitParam(name = "amount", value = "金额", required = true, example = "1", dataTypeClass = Integer.class),
            @ApiImplicitParam(name = "subject", value = "商品描述", required = true,example = "商品", dataTypeClass = String.class), // 多个使用空格分隔
            @ApiImplicitParam(name = "body", value = "商品标题",required = true, example = "商品", dataTypeClass = String.class),
            @ApiImplicitParam(name = "notifyUrl", value = "通知地址", required = true,example = "http://baidu.com", dataTypeClass = String.class),

    })
    public PayCommonResult<PayOrderUnifiedRespDTO> submitPayApiOrder(@Validated @RequestBody AppPayOrderCommSubmitReqVO reqVO) {
        return orderService.submitCommonPayOrder(reqVO, getClientIP());
    }

    @GetMapping("/get-detail")
    @ApiOperation(value = "获得支付订单详情")
    @ApiImplicitParam(name = "id", required = true, value = "编号", example = "1002012", dataTypeClass = Long.class)
    public CommonResult<PayOrderDetailsRespVO> getOrderDetail(@RequestParam("id") BigInteger id) {
        PayOrderDO order = orderService.getOrder(id);
        if (ObjectUtil.isNull(order)) {
            return success(new PayOrderDetailsRespVO());
        }

        PayMerchantDO merchantDO = merchantService.getMerchant(order.getMerchantId());
        PayAppDO appDO = appService.getApp(order.getAppId());
        PayChannelEnum channelEnum = PayChannelEnum.getByCode(order.getChannelCode());

        // TODO @aquan：文案，都是前端 format；
        PayOrderDetailsRespVO respVO = PayOrderConvert.INSTANCE.orderDetailConvert(order);
        respVO.setMerchantName(ObjectUtil.isNotNull(merchantDO) ? merchantDO.getName() : "未知商户");
        respVO.setAppName(ObjectUtil.isNotNull(appDO) ? appDO.getName() : "未知应用");
        respVO.setChannelCodeName(ObjectUtil.isNotNull(channelEnum) ? channelEnum.getName() : "未知渠道");

        PayOrderExtensionDO extensionDO = orderExtensionService.getOrderExtension(order.getSuccessExtensionId());
        if (ObjectUtil.isNotNull(extensionDO)) {
            respVO.setPayOrderExtension(PayOrderConvert.INSTANCE.orderDetailExtensionConvert(extensionDO));
        }

        return success(respVO);
    }

    @TenantIgnore
    @PermitAll
    @PostMapping("/orderStatus")
    @ApiOperation(value = "支付订单状态查询")
    public PayCommonResult<PayOrderStatusRespDTO> orderStatus(@Validated @RequestBody PayOrderStatusReqDTO reqVO ) {
        PayOrderDO payOrderDO = payOrderMapper.selectByAppIdAndMerchantOrderId(reqVO.getAppId(), reqVO.getMerchantOrderId());
        return PayCommonResult.success(PayOrderConvert.INSTANCE.orderStatusConvert(payOrderDO));
    }

    /**
     * 交易状态查询，先查询本地，若支付成功则返回本地数据，否则查询支付渠道商的接口
     * @param reqVO 请求参数
     * @return
     */
    @TenantIgnore
    @PermitAll
    @PostMapping("/tradeQuery")
    @ApiOperation(value = "交易状态查询")
    public CommonResult<PayOrderStatusRespDTO> tradeQuery(@Validated @RequestBody PayOrderStatusReqDTO reqVO ) {
        PayOrderDO payOrderDO = payOrderMapper.selectByAppIdAndMerchantOrderId(reqVO.getAppId(), reqVO.getMerchantOrderId());
        if (payOrderDO == null){
            String message = ServiceExceptionUtil.doFormat(PAY_ORDER_WRONG_ARGS.getCode(), PAY_ORDER_WRONG_ARGS.getMsg(), reqVO.getAppId(), reqVO.getMerchantOrderId());
            log.warn("tradeQuery : {}",message);
            return error(PAY_ORDER_WRONG_ARGS.getCode(), message);
        }
        if (PayOrderStatusEnum.isSuccess(payOrderDO.getStatus())){
            return success(PayOrderConvert.INSTANCE.orderStatusConvert(payOrderDO));
        } else {
            PayClient payClient = channelService.getPayClient(payOrderDO.getChannelId());
            PayOrderUnifiedRespDTO order = payClient.getOrder(payOrderDO.getNo());
            PayOrderStatusRespDTO respDTO = new PayOrderStatusRespDTO();
            respDTO.setMerchantOrderId(payOrderDO.getMerchantOrderId());
            respDTO.setStatus(order.getStatus());
            respDTO.setTradeStatus(order.getTradeStatus());
            Optional.ofNullable(order.getSuccessTime()).ifPresent(time -> respDTO.setSuccessTime(LocalDateTimeUtil.parse(time,NORM_DATETIME_PATTERN)));
            respDTO.setNo(order.getTradeNo());
            return success(respDTO);
        }
    }


    @GetMapping("/page")
    @ApiOperation(value = "获得支付订单分页")
    public CommonResult<PageResult<PayOrderPageItemRespVO>> getOrderPage(@Valid PayOrderPageReqVO pageVO) {
        PageResult<PayOrderDO> pageResult = orderService.getOrderPage(pageVO);
        if (CollectionUtil.isEmpty(pageResult.getList())) {
            return success(new PageResult<>(pageResult.getTotal()));
        }

        // 处理商户ID数据
        Map<BigInteger, PayMerchantDO> merchantMap = merchantService.getMerchantMap(
                CollectionUtils.convertList(pageResult.getList(), PayOrderDO::getMerchantId));
        // 处理应用ID数据
        Map<BigInteger, PayAppDO> appMap = appService.getAppMap(
                CollectionUtils.convertList(pageResult.getList(), PayOrderDO::getAppId));

        List<PayOrderPageItemRespVO> pageList = new ArrayList<>(pageResult.getList().size());
        pageResult.getList().forEach(c -> {
            PayMerchantDO merchantDO = merchantMap.get(c.getMerchantId());
            PayAppDO appDO = appMap.get(c.getAppId());
            PayChannelEnum channelEnum = PayChannelEnum.getByCode(c.getChannelCode());

            PayOrderPageItemRespVO orderItem = PayOrderConvert.INSTANCE.pageConvertItemPage(c);
            orderItem.setMerchantName(ObjectUtil.isNotNull(merchantDO) ? merchantDO.getName() : "未知商户");
            orderItem.setAppName(ObjectUtil.isNotNull(appDO) ? appDO.getName() : "未知应用");
            orderItem.setChannelCodeName(ObjectUtil.isNotNull(channelEnum) ? channelEnum.getName() : "未知渠道");
            pageList.add(orderItem);
        });
        return success(new PageResult<>(pageList, pageResult.getTotal()));
    }

}
